﻿using System;

namespace MathService.Temp
{
    /// <summary>Y-комбинатор</summary>
    [Copyright("lambdy.ru")]
    internal static class Y_Combinator
    {
        private delegate T y<T>(y<T> F);

        /// <summary>Y = (λh.λF.F(λx.((h(h))(F))(x))) (λh.λF.F(λx.((h(h))(F))(x)))</summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="f"></param>
        /// <returns></returns>
        private static Func<TIn, TOut> Y<TIn, TOut>(Func<Func<TIn, TOut>, Func<TIn, TOut>> f)
        {
            var yy = new y<Func<TIn, TOut>>(h => F => f(h(h))(F));

            var y1 = yy(yy);

            return new y<Func<TIn, TOut>>(h => F => f(h(h))(F))(h => F => f(h(h))(F));
        }

        private static Func<int, int> Factorial(Func<int, int> factorial) { return n => (n == 0) ? 1 : n * factorial(n - 1); }

        public static void Test()
        {
            var factorial = Y<int, int>(Factorial);
            Console.WriteLine("6! = {0}", factorial(6));
        }
    }
}
